package znet

import (
	"errors"
	"fmt"
	"gin-ranking/common/zinx/utils"
	"gin-ranking/common/zinx/ziface"
	"net"
	"sync"
)

type Connection struct {
	// 当前 conn 属于哪个 Server
	TcpServer ziface.IServer

	// 当前连接
	Conn *net.TCPConn

	// 连接id
	ConnID uint32

	// 当前连接状态
	isClosed bool

	//// 当前处理业务的方法API
	//Router ziface.IRouter

	// 消息的管理 MsgID 和对应处理业务 API 关系
	MsgHandler ziface.IMsgHandle

	// 通知当前连接停止的 channel
	ExitChan chan bool

	// 无缓冲的管道，用于读、写Goroutine之间的通信
	msgChan chan []byte

	// 连接属性集合
	property map[string]interface{}

	// 保护连接属性的锁
	propertyLock sync.RWMutex
}

// StartReader 连接的读业务方法
func (c *Connection) StartReader() {
	fmt.Println("[Reader Goroutine is running]")
	defer fmt.Println("connID = ", c.ConnID, ", Reader is exit, remote addr is ", c.RemoteAddr().String()) // 2
	defer c.Stop()

	for {
		// 创建一个拆包对象
		//dp := NewDataPack()

		//// 得到当前 connection 的request数据
		//headData := make([]byte, dp.GetHeadLen())
		//if _, err := io.ReadFull(c.Conn, headData); err != nil {
		//	fmt.Println("read data err:", err)
		//	break
		//}
		//
		//// 读取客户端的Msg Head 二级字节流 8个字节
		//msg, err := dp.Unpack(headData)
		//if err != nil {
		//	fmt.Println("unpack data err:", err)
		//	break
		//}

		// 拆包 得到 msgId 个 msgDataLen 放入 msg.data 消息中
		data := make([]byte, 1024)
		//if msg.GetMsgLen() > 0 {
		//	data = make([]byte, msg.GetMsgLen())
		_, err := c.Conn.Read(data)
		if err != nil {
			fmt.Println("read data err:", err)
			break
		}
		//}

		msg := NewMsgPackage(0, data)

		//msg.SetData(data)

		// 得到当前的 conn 数据的 request 数据
		req := Request{
			conn: c,
			msg:  msg,
		}

		if utils.GlobalObject.WorkerPoolSize > 0 {
			// 已经开启工作池，将消息发送给工作池
			c.MsgHandler.SendMsgToTaskQueue(&req)
		} else {
			// 从路由中，找到注册绑定的COnn对应的Router调用
			// 根据绑定好的MsgID找到处理对应API业务 执行
			go c.MsgHandler.DoMsgHandler(&req)
		}
	}
}

// StartWriter 写消息Goroutine，专门发送给客户端消息的模块
func (c *Connection) StartWriter() {
	fmt.Println("[Writer Goroutine is running]")
	defer fmt.Println("connID = ", c.ConnID, ", Writer is exit, remote addr is ", c.RemoteAddr().String()) // 2
	// 不断的阻塞等待channel消息，进行写给客户端
	for {
		select {
		case data := <-c.msgChan:
			// 有数据要写给客户端
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("write data err:", err)
				return
			}
		case <-c.ExitChan:
			// Reader 已经退出，此时 Writer 也要退出
			return
		}
	}
}

// SendMsg 将要发送给客户端的数据，先进行封包再发送
func (c *Connection) SendMsg(data []byte) error {
	if c.isClosed {
		return errors.New("connection closed when send msg")
	}
	// 将data进行封包 MsgDataLen/MsgID/Data
	//dp := NewDataPack()
	//// MsgDataLen/MsgId/Data
	//binaryMsg, err := dp.Pack(NewMsgPackage(msgId, data))
	//if err != nil {
	//	fmt.Println("pack error msg is = ", msgId)
	//	return errors.New("pack err msg")
	//}
	//
	//binaryMsg := NewMsgPackage(0, data)

	// 读写分离：将数据发送给消息管道
	//if _, err := c.Conn.Write(data); err != nil {
	//	fmt.Println("Write msg is error", err)
	//	return errors.New("conn write msg err")
	//}

	// 读写分离：将数据发送给消息管道
	c.msgChan <- data
	return nil
}

// Start 启动连接 - 让当前的连接准备开始工作
func (c *Connection) Start() {
	fmt.Println("Conn Start()... ConnId = ", c.ConnID)
	// 启动从当前连接读数据的业务
	go c.StartReader()
	// 启动从当前连接写数据的业务
	go c.StartWriter()

	// 执行开发者注册的 OnConnStart 钩子函数
	c.TcpServer.CallOnConnStart(c)
}

// Stop 停止连接 - 结束当前连接的工作
func (c *Connection) Stop() {

	fmt.Println("Conn Stop()... ConnID = ", c.ConnID)
	if c.isClosed {
		return
	}
	c.isClosed = true

	// 调用开发者注册的 OnConnStop 钩子函数
	c.TcpServer.CallOnConnStop(c)

	// 告知 Writer 关闭
	c.ExitChan <- true

	// 关闭socket连接
	c.Conn.Close()

	// 将当前连接从 ConnMgr 中删除
	c.TcpServer.GetConnMgr().Remove(c)

	// 回收资源
	close(c.ExitChan)
	close(c.msgChan)
}

// GetTCPConnection 获取当前连接绑定的 socket conn
func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

// GetConnID 获取当前连接的连接ID
func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}

// RemoteAddr 获取远程客户端的 TCP状态 IP Port
func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

// SetProperty 设置连接属性
func (c *Connection) SetProperty(key string, value interface{}) {

	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()

	// 添加连接属性
	if len(key) > 0 && value != nil {
		c.property[key] = value
	}
}

// GetProperty 获取连接属性
func (c *Connection) GetProperty(key string) (interface{}, error) {

	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	if value, ok := c.property[key]; ok {
		return value, nil
	} else {
		return nil, errors.New("no property found")
	}
}

// RemoveProperty 移除连接属性
func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	// 删除连接属性
	delete(c.property, key)

}

// NewConnection 初始化连接模块的方法
func NewConnection(server ziface.IServer, conn *net.TCPConn, connID uint32, msgHandle ziface.IMsgHandle) *Connection {
	c := &Connection{
		TcpServer:  server,
		Conn:       conn,
		ConnID:     connID,
		isClosed:   false,
		MsgHandler: msgHandle,
		ExitChan:   make(chan bool, 1),
		msgChan:    make(chan []byte),
		property:   map[string]interface{}{},
	}

	// 将 conn 加入到 ConnManager 中
	c.TcpServer.GetConnMgr().Add(c)

	return c
}
